package com.xpec.c4.db.hibernate.manager;

import java.lang.management.ManagementFactory;
import java.sql.Connection;

import javax.management.MBeanServer;
import javax.management.ObjectName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.jmx.StatisticsService;

/**
 * 数据库连接管理
 * 
 * @author GuoQi E-mail: guoqilianling@hotmail.com
 *@version 创建时间: 2010-12-29下午02:34:34
 */
public class HibernateManager {
	/**
	 * log
	 */
	private static Log log = LogFactory.getLog(HibernateManager.class);

	/**
	 * Session Factory
	 */
	private static SessionFactory sessionFactory;

	/**
	 * Threadlocal Session
	 */
	private static final ThreadLocal<Session> session = new ThreadLocal<Session>();

	/**
	 * Transaction
	 */
	private static final ThreadLocal<Transaction> transaction = new ThreadLocal<Transaction>();

	/**
	 * @param connectBean
	 * @return
	 * @throws Exception 
	 */
	public static Session getSession(){
		Session s = session.get();

		if (s == null) {

			try {
				if (sessionFactory == null) {
					sessionFactory = ConfigurationManager
							.getHibernateConfiguration(ManagerType.GAME)
							.buildSessionFactory();
					log.info("load default db cfg:"	+ ManagerType.GAME);
					
					if (!ConfigurationManager.isAutoCreateTables()) {
						StatisticsService statsMBean = new StatisticsService();
						statsMBean.setSessionFactory(sessionFactory);
						statsMBean.setStatisticsEnabled(true);

						MBeanServer mBeanServer = ManagementFactory
								.getPlatformMBeanServer();
						int hashCode = HibernateManager.class.getClassLoader()
								.hashCode();
						mBeanServer
								.registerMBean(statsMBean, new ObjectName(
										"Hibernate:application=hibernate1 "
												+ hashCode));
					}
				}

				/*
				 * get the session
				 */
				s = sessionFactory.openSession();

			} catch (Exception e) {
				log.error("Error occurred when open a new session", e);
				throw new RuntimeException(e);
			}
		}

		session.set(s);
		s.clear();

		return s;
	}

	/**
	 * 关闭session
	 * 
	 * @throws HibernateException
	 */
	public static void closeSession() throws HibernateException {
		Session s = session.get();
		session.set(null);
		transaction.set(null);

		if (s != null) {
			try {
				s.close();
			} catch (HibernateException e) {
				log.error("Error closing the session", e);
				throw e;
			}
		}
	}

	/**
	 * 关闭session
	 * 
	 * @throws HibernateException
	 */
	public static void endSession() throws HibernateException {
		closeSession();
	}

	/**
	 * 事务开始
	 * 
	 * @throws HibernateException
	 */
	public static Session txBegin() throws HibernateException {
		Session s = null;
		try {
			s = getSession();
			Transaction t = s.beginTransaction();
			transaction.set(t);

		} catch (HibernateException e) {
			log.error("Error beginning a transaction: ", e);
			throw e;
		}

		return s;
	}

	/**
	 * 事务提交
	 * 
	 * @return
	 * @throws HibernateException
	 */
	public static boolean txCommit() throws HibernateException{
		Session s = session.get();
		Transaction t = transaction.get();

		if (s == null || t == null) {
			return false;
		} else {
			try {
				t.commit();
			} catch (HibernateException e) {
				log.error("Error committing transaction: ", e);
				throw e;
			}
		}

		return true;
	}

	/**
	 * 事务回滚
	 * 
	 * @return
	 * @throws HibernateException
	 */
	public static boolean txRollback() throws HibernateException {
		Session s = session.get();
		Transaction t = transaction.get();

		if (s == null || t == null) {
			return false;
		} else {
			try {
				t.rollback();
			} catch (HibernateException e) {
				log.error("Error rollbacking transaction: ", e);
				throw e;
			}
		}

		return true;
	}

	public static boolean txCommitFailRollbackClearSession() {
		return txCommitFailRollbackClearSession(null);
	}

	/**
	 * @author jizhichao XPEC
	 * @param errorCode
	 *            失敗錯誤碼會存入errorCode[0]<BR>
	 *            0:成功;<BR>
	 *            非0正整數:Mysql錯誤,錯誤碼請參考MysqlErrorNumbers<BR>
	 *            -1:非Mysql錯誤;<BR>
	 *            (注意:帶入errorCode必須使用者自行new int[0]或{0},否則errorCode仍會回傳null)<BR>
	 * <BR>
	 *            範例:<BR>
	 *            int[] errorCode = new int[1];<BR>
	 *            txCommitFailRollbackClearSession(errorCode);<BR>
	 *            if (errorCode[0] == MysqlErrorNumbers.ER_DUP_ENTRY)<BR>
	 *            System.out.println("Save duplicate key data!!");<BR>
	 * @return
	 */
	public static boolean txCommitFailRollbackClearSession(int[] errorCode) {
		try {
			txCommit();
			
			if (errorCode != null && errorCode.length != 0)
				errorCode[0] = 0;
			return true;
		} catch (JDBCException e) {
			txRollback();
			log.error("txCommitFailRollbackClearSession Transaction commit fail JDBCException ! ", e);
			
			if (errorCode != null && errorCode.length != 0)
				errorCode[0] = e.getErrorCode();
			return false;
		} catch (HibernateException e) {
			txRollback();
			log.error("txCommitFailRollbackClearSession Transaction commit fail HibernateException ! ", e);
			
			if (errorCode != null && errorCode.length != 0)
				errorCode[0] = -1;
			return false;
		} catch (Exception e) {
			txRollback();
			log.error("txCommitFailRollbackClearSession Transaction commit fail Exception ! ", e);
			return false;
		}finally {
		
			// clearSession();
			endSession();
		}
	}
	/**
	 * 還沒到txCommitFailRollbackClearSession就噴錯, 要將其還原
	 * @param e
	 * @param errorCode
	 * @return
	 */
	public static boolean txOperationFailRollbackClearSession(Exception e , int[] errorCode) {
		if (e instanceof JDBCException) {
			txRollback();
			log.error("txOperationFailRollbackClearSession Transaction commit fail JDBCException ! ", e);
			
			if (errorCode != null && errorCode.length != 0)
				errorCode[0] = ((JDBCException)e).getErrorCode();
			
		}
		else if (e instanceof HibernateException) {
			txRollback();
			log.error("txOperationFailRollbackClearSession Transaction commit fail HibernateException ! ", e);
			if (errorCode != null && errorCode.length != 0)
				errorCode[0] = -1;
			
		} 
		endSession();
		return false;
	}
	/**
	 * get the current database connection.
	 * 
	 * @return
	 * @throws EPSTxException
	 */
	@SuppressWarnings("deprecation")
	public static Connection getConnection() throws HibernateException {
		try {
			return getSession().connection();
		} catch (HibernateException e) {
			log.error("Error getting the db connection: ", e);
			throw e;
		}
	}

	/**
	 * 清空Session
	 */
	public static void clearSession() {
		Session session = getSession();
		session.clear();
	}

	/**
	 * 更新Session
	 */
	public static void flushSession() {
		Session session = getSession();
		session.flush();
	}

	/**
	 * PO重新关联到session，状态由原来的脱管态变为游离态
	 */
	public static void rePersistent(Object obj) {
		if (obj == null)
			return;
		Session session = getSession();
		try {
			session.lock(obj, LockMode.NONE);
		} catch (HibernateException e) {
			try {
				if (session.contains(obj))
					session.evict(obj);
			} catch (Exception e1) {
			}
		}
	}

	public static void sessionEvict(Object obj) {
		Session session = getSession();
		session.evict(obj);
	}

	/**
	 * 获得数据库的名称
	 * 
	 * @return
	 */
	public static String getSchemaName() {
		return getSchemaName(ManagerType.GAME);
	}

	/**
	 * 获得数据库的名称
	 * 
	 * @return
	 */
	public static String getSchemaName(ManagerType type) {

		String url = ConfigurationManager.getConnectionUrl(type);
		int firstIndex = url.lastIndexOf("/") + 1;
		int lastIndex = url.lastIndexOf("?");
		String schemaName = "";
		if (lastIndex > 0) {
			schemaName = url.substring(firstIndex, lastIndex);
		} else {
			schemaName = url.substring(firstIndex);
		}
		return schemaName;
	}
	
	public static void destroySessionFactory() {
		if(sessionFactory == null || sessionFactory.isClosed()) return;
		closeSession();
		sessionFactory.close();
		sessionFactory = null;
	}
}
